package com.borealis.common.utils.setting;


import com.google.common.collect.Maps;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

/**
 * redis设置虚拟类
 *
 * @author yaoweixin
 * @date 2018/10/15
 */
public abstract class BaseRedisSettings {

  /**
   * 获取设置名称
   *
   * @return 名称
   */
  protected abstract String getSetName();

  /**
   * 获得所有配置
   *
   * @param key 键名
   * @return 默认配置
   */
  protected abstract Map<String, Object> getAllSettings(String key);

  /**
   * 设置配置
   *
   * @param key   键名
   * @param field 域
   * @param value 值
   */
  protected abstract void putSetting(String key, String field, Object value);

  /**
   * 获得默认配置
   *
   * @return 默认配置
   */
  protected abstract Map<String, Object> getDefault();

  /**
   * 获取设置超时时间
   *
   * @return 时间
   */
  protected abstract long getSettingTimeout();

  public Optional<Object> getSetting(String name) {
    retrieveRuntime();
    return Optional.ofNullable(settings.getOrDefault(name, null));
  }

  /**
   * 获取当前时间（秒数）
   *
   * @return 时间
   */
  public long currentTime() {
    return System.currentTimeMillis() / 1000;
  }

  /**
   * 配置集合
   */
  protected Map<String, Object> settings = null;
  /**
   * 最后更新时间
   */
  private long lastRetrieve = 0;

  protected void retrieveRuntime() {
    if (settings == null || currentTime() - lastRetrieve > getSettingTimeout()) {
      String set = getSetName();
      settings = getAllSettings(set);
      if (settings.isEmpty()) {
        settings = getDefault();
        for (Map.Entry<String, Object> entry : settings.entrySet()) {
          putSetting(set, entry.getKey(), entry.getValue());
        }
      }
      lastRetrieve = currentTime();
    }
  }

  /**
   * 配置集合
   */
  protected static Map<String, Object> configs = Maps.newConcurrentMap();

  public static Object retrieveConfig(String name, Function<String, Object> function) {
    return retrieveConfig(name, function, null, false);
  }

  public static Object retrieveConfig(String name, Function<String, Object> function, String param,
      boolean retrieve) {
    Object obj;
    if (retrieve || !configs.containsKey(name)) {
      obj = function.apply(param);
      configs.put(name, obj);
    } else {
      obj = configs.get(name);
    }
    return obj;
  }
}
